﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Budget.Core.DomainModel;
using ConfOrm;
using ConfOrm.NH;
using ConfOrm.Patterns;
using ConfOrm.Shop.CoolNaming;
using ConfOrm.Shop.DearDbaNaming;
using ConfOrm.Shop.Inflectors;
using NHibernate.Cfg.MappingSchema;

namespace Budget.Data.Mappings
{
    public class DomainMapper
    {
        public HbmMapping GenerateMappings()
        {
            var orm = new ObjectRelationalMapper();

            var domainEntities = GetDomainEntities();

            orm.TablePerConcreteClass(domainEntities);

            orm.Patterns.PoidStrategies.Add(new NativePoidPattern());

            AddCascade(orm);
            
            var patternsAppliers = new CoolPatternsAppliersHolder(orm);
            patternsAppliers.Merge(new ClassPluralizedTableApplier(new EnglishInflector()));
            
            var mapper = new Mapper(orm, patternsAppliers);

            HbmMapping mapping = mapper.CompileMappingFor(domainEntities);

            File.WriteAllText(@"C:\mapping-budget.xml", Serialize(mapping));

            return mapping;
        }

        private void AddCascade(ObjectRelationalMapper orm)
        {
            orm.Cascade<Category, Product>(CascadeOn.All);
            orm.Cascade<Product, Category>(CascadeOn.Persist);
            orm.Cascade<IncomeOrder, IncomeType>(CascadeOn.All);
            orm.Cascade<OutcomeOrder, Product>(CascadeOn.All);
        }

        private IEnumerable<Type> GetDomainEntities()
        {
            Assembly ass = typeof(BaseEntity).Assembly;
            var domainEntities = ass.GetTypes().Where(t => typeof(BaseEntity).IsAssignableFrom(t) && !t.IsGenericType && t != typeof(BaseEntity));
            return domainEntities;
        }

        private string Serialize(HbmMapping hbmElement)
        {
            var setting = new XmlWriterSettings { Indent = true };
            var serializer = new XmlSerializer(typeof(HbmMapping));
            using (var memStream = new MemoryStream())
            {
                using (var xmlWriter = XmlWriter.Create(memStream, setting))
                {
                    serializer.Serialize(xmlWriter, hbmElement);
                    memStream.Flush();
                    byte[] streamContents = memStream.ToArray();

                    string result = Encoding.UTF8.GetString(streamContents);
                    return result;
                }
            }
        }
    }
}